package com.linskating.wheat.common;

import com.linskating.wheat.constant.UserConstant;
import com.linskating.wheat.exception.BusinessException;
import com.linskating.wheat.model.domain.User;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;


@Aspect
@Slf4j
@Component
public class LogAspect implements UserConstant {

    private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);

    @Pointcut("@within(com.linskating.wheat.annotations.aop.LogOperation) || " +
            "@annotation(com.linskating.wheat.annotations.aop.LogOperation)")
    public void loggableMethods() {
    }


    // 假如在该方法参数上HttpServletRequest request，假如有些方法没有此参数，就会导致aop失败
    // Spring AOP 在解析通知方法的参数时，会根据切点表达式和目标方法的参数类型进行匹配
    @Around("loggableMethods()")
    public Object aroundLog(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String methodName = signature.getMethod().getName();
        // 通过RequestContextHolder.currentRequestAttributes()).getRequest()获取当前请求
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();

        User currentUser = (User) request.getSession().getAttribute(USER_LOGIN_STATUS);

        if (currentUser == null) {
            String ipAddress = getClientIpAddress(request);
            logger.info("操作人ip地址: {}", ipAddress);
        } else {
            logger.info("用户账号：{}  用户id：{}", currentUser.getUserAccount(), currentUser.getId());
            logger.info("Before request: {}", methodName);
        }
        long startTime = System.currentTimeMillis();
        Object result;
        try {
            result = joinPoint.proceed();
        } catch (BusinessException throwable) {
            logger.error("Exception in method: {} with exception: {}", signature.getMethod().getName(), throwable.getMessage());
            throw throwable;
        }
        long endTime = System.currentTimeMillis();

        logger.info("After request: {} took {} ms", methodName, endTime - startTime);
        logger.info("----------------------------------------------------");
        return result;
    }

    private String getClientIpAddress(HttpServletRequest request) {
        // 尝试从请求头中获取 X-Forwarded-For 或 X-Real-IP
        String ipAddress = request.getHeader("X-Forwarded-For");
        if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("X-Real-IP");
        }

        // 如果请求头中没有找到，使用 getRemoteAddr() 获取
        if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
        }

        // 如果 X-Forwarded-For 中包含多个 IP（以逗号分隔），取第一个
        if (ipAddress != null && ipAddress.contains(",")) {
            ipAddress = ipAddress.split(",")[0].trim();
        }

        return ipAddress;
    }
}
